From dafec35ec5815c6319d3b5ad7f10616857cc694c Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Sat, 19 Aug 2017 23:43:32 +0200 Subject: [PATCH] babl: proof icc parsing against corrupt data --- babl/babl-icc.c | 224 +++++++++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 100 deletions(-) diff --git a/babl/babl-icc.c b/babl/babl-icc.c index 7fd9b32..129368b 100644 --- a/babl/babl-icc.c +++ b/babl/babl-icc.c @@ -1,3 +1,21 @@ +/* babl - dynamically extendable universal pixel conversion library. + * Copyright (C) 2017, Øyvind Kolås. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see + * . + */ + #include "config.h" #include "babl-internal.h" #include @@ -6,80 +24,85 @@ #define ICC_HEADER_LEN 128 #define TAG_COUNT_OFF ICC_HEADER_LEN -static int load_byte (const char *icc, int offset) +static int load_byte (const char *icc, int length, int offset) { +/* all reading functions take both the char *pointer and the length of the + * buffer, and all reads thus gets protected by this condition. + */ + if (offset < 0 || offset > length) + return 0; + return *(uint8_t*) (&icc[offset]); } -static int16_t load_u1Fixed15 (const char *icc, int offset) +static int load_sbyte (const char *icc, int length, int offset) +{ + if (offset < 0 || offset > length) + return 0; + + return *(int8_t*) (&icc[offset]); +} + +static int16_t load_u1f15 (const char *icc, int length, int offset) { - return load_byte (icc, offset + 1) + - (load_byte (icc, offset + 0) << 8); + return load_sbyte (icc, length, offset + 1) + + (load_byte (icc, length, offset + 0) << 8); } -static uint16_t load_uint16 (const char *icc, int offset) +static uint16_t load_u16 (const char *icc, int length, int offset) { - return load_byte (icc, offset + 1) + - (load_byte (icc, offset + 0) << 8); + return load_byte (icc, length, offset + 1) + + (load_byte (icc, length, offset + 0) << 8); } -static double load_s15Fixed16 (const char *icc, int offset) +static double load_s15f15 (const char *icc, int length, int offset) { - return load_u1Fixed15 (icc, offset) + load_uint16 (icc, offset + 2) / 65535.0f; + return load_u1f15 (icc, length, offset) + + load_u16 (icc, length, offset + 2) / 65535.0f; } -static double load_u16Fixed16 (const char *icc, int offset) +static double load_u16f16 (const char *icc, int length, int offset) { - return load_uint16 (icc, offset) + load_uint16 (icc, offset + 2) / 65535.0; + return load_u16 (icc, length, offset) + + load_u16 (icc, length, offset + 2) / 65535.0; } -static uint32_t load_uint32 (const char *icc, int offset) +static uint32_t load_u32 (const char *icc, int length, int offset) { - return load_byte (icc, offset + 3) + - (load_byte (icc, offset + 2) << 8) + - (load_byte (icc, offset + 1) << 16) + - (load_byte (icc, offset + 0) << 24); + return load_byte (icc, length, offset + 3) + + (load_byte (icc, length, offset + 2) << 8) + + (load_byte (icc, length, offset + 1) << 16) + + (load_byte (icc, length, offset + 0) << 24); } -static void load_sign (const char *icc, int offset, char *sign) +static void load_sign (const char *icc, int length, + int offset, char *sign) { - sign[0]=load_byte(icc, offset); - sign[1]=load_byte(icc, offset + 1); - sign[2]=load_byte(icc, offset + 2); - sign[3]=load_byte(icc, offset + 3); + sign[0]=load_byte(icc, length, offset); + sign[1]=load_byte(icc, length, offset + 1); + sign[2]=load_byte(icc, length, offset + 2); + sign[3]=load_byte(icc, length, offset + 3); sign[4]=0; } -/* looks up offset and length for a specifi icc tag +/* looks up offset and length for a specific icc tag */ -static int icc_tag (const char *icc, const char *tag, int *offset, int *length) +static int icc_tag (const char *icc, int length, + const char *tag, int *offset, int *el_length) { - int tag_count = load_uint32 (icc, TAG_COUNT_OFF); - int profile_size = load_uint32 (icc, 0); + int tag_count = load_u32 (icc, length, TAG_COUNT_OFF); int t; for (t = 0; t < tag_count; t++) { char tag_signature[5]; - load_sign (icc, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); if (!strcmp (tag_signature, tag)) { - if (!offset) - return 1; - *offset = load_uint32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4); - /* avert some potential for maliciousnes.. */ - if (*offset >= profile_size) - { - *offset = profile_size - 1; - } - if (!length) - return 1; - *length = load_uint32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4 * 2); - /* avert some potential for maliciousnes.. */ - if (*offset + *length >= profile_size) - { - *length = profile_size - *offset - 1; - } + if (offset) + *offset = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4); + if (el_length) + *el_length = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4*2); return 1; } } @@ -92,7 +115,7 @@ static const Babl *babl_trc_from_icc (const char *icc, { int offset = 0; { - int count = load_uint32 (icc, offset + 8); + int count = load_u32 (icc, length, offset + 8); int i; { if (count == 0) @@ -101,8 +124,8 @@ static const Babl *babl_trc_from_icc (const char *icc, } else if (count == 1) { - return babl_trc_gamma (load_byte (icc, offset + 12) + - load_byte (icc, offset + 12 + 1) / 255.0); + return babl_trc_gamma (load_byte (icc, length, offset + 12) + + load_byte (icc, length, offset + 12 + 1)/255.0); } else { @@ -113,9 +136,10 @@ static const Babl *babl_trc_from_icc (const char *icc, for (i = 0; i < count && i < 10; i ++) { - fprintf (stdout, "%i=%i ", i, load_uint16 (icc, offset + 12 + i * 2)); + fprintf (stdout, "%i=%i ", i, load_u16 (icc, length, + offset + 12 + i * 2)); if (i % 7 == 0) - fprintf (stdout, "\n"); + fprintf (stdout, "\n"); } } } @@ -128,11 +152,11 @@ babl_space_rgb_icc (const char *icc, int length, char **error) { - int profile_size = load_uint32 (icc, 0); - int icc_ver_major = load_byte (icc, 8); - const Babl *trc_red = NULL; + int profile_size = load_u32 (icc, length, 0); + int icc_ver_major = load_byte (icc, length, 8); + const Babl *trc_red = NULL; const Babl *trc_green = NULL; - const Babl *trc_blue = NULL; + const Babl *trc_blue = NULL; char profile_class[5]; char color_space[5]; @@ -146,13 +170,13 @@ babl_space_rgb_icc (const char *icc, *error = "only ICC v2 profiles supported"; return NULL; } - load_sign (icc, 12, profile_class); + load_sign (icc, length, 12, profile_class); if (strcmp (profile_class, "mntr")) { *error = "not a monitor-class profile"; return NULL; } - load_sign (icc, 16, color_space); + load_sign (icc, length, 16, color_space); if (strcmp (color_space, "RGB ")) { *error = "not defining an RGB space"; @@ -160,23 +184,20 @@ babl_space_rgb_icc (const char *icc, } { int offset, element_size; - if (icc_tag (icc, "rTRC", &offset, &element_size)) + if (icc_tag (icc, length, "rTRC", &offset, &element_size)) { trc_red = babl_trc_from_icc (icc + offset, element_size, error); - if (*error) - return NULL; + if (*error) return NULL; } - if (icc_tag (icc, "gTRC", &offset, &element_size)) + if (icc_tag (icc, length, "gTRC", &offset, &element_size)) { trc_green = babl_trc_from_icc (icc + offset, element_size, error); - if (*error) - return NULL; + if (*error) return NULL; } - if (icc_tag (icc, "bTRC", &offset, &element_size)) + if (icc_tag (icc, length, "bTRC", &offset, &element_size)) { trc_blue = babl_trc_from_icc (icc + offset, element_size, error); - if (*error) - return NULL; + if (*error) return NULL; } } @@ -186,37 +207,40 @@ babl_space_rgb_icc (const char *icc, return NULL; } - if (icc_tag (icc, "chrm", NULL, NULL) && - icc_tag (icc, "wtpt", NULL, NULL)) + if (icc_tag (icc, length, "chrm", NULL, NULL) && + icc_tag (icc, length, "wtpt", NULL, NULL)) { int offset, element_size; double redX, redY, greenX, greenY, blueX, blueY; + int channels, phosporant; - icc_tag (icc, "chrm", &offset, &element_size); - { - int channels = load_uint16 (icc, offset + 8); - int phosporant = load_uint16 (icc, offset + 10); - - redX = load_s15Fixed16 (icc, offset + 12); - redY = load_s15Fixed16 (icc, offset + 12 + 4); - greenX = load_s15Fixed16 (icc, offset + 20); - greenY = load_s15Fixed16 (icc, offset + 20 + 4); - blueX = load_s15Fixed16 (icc, offset + 28); - blueY = load_s15Fixed16 (icc, offset + 28 + 4); + icc_tag (icc, length, "chrm", &offset, &element_size); + channels = load_u16 (icc, length, offset + 8); + phosporant = load_u16 (icc, length, offset + 10); - fprintf (stdout, "chromaticity:\n"); - fprintf (stdout, " channels: %i\n", channels); - fprintf (stdout, " phosphorant: %i\n", phosporant); - fprintf (stdout, " CIE xy red: %f %f\n", redX, redY); - fprintf (stdout, " CIE xy green: %f %f\n", greenX, greenY); - fprintf (stdout, " CIE xy blue: %f %f\n", blueX, blueY); + if (phosporant != 0) + { + *error = "unhandled phosporants, please report bug"; + return NULL; } + if (channels != 3) + { + *error = "unexpected non 3 count of channels"; + return NULL; + } + + redX = load_s15f15 (icc, length, offset + 12); + redY = load_s15f15 (icc, length, offset + 12 + 4); + greenX = load_s15f15 (icc, length, offset + 20); + greenY = load_s15f15 (icc, length, offset + 20 + 4); + blueX = load_s15f15 (icc, length, offset + 28); + blueY = load_s15f15 (icc, length, offset + 28 + 4); - icc_tag (icc, "wtpt", &offset, &element_size); + icc_tag (icc, length, "wtpt", &offset, &element_size); { - double wX = load_u16Fixed16 (icc, offset + 8); - double wY = load_u16Fixed16 (icc, offset + 8 + 4); - double wZ = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + double wX = load_u16f16 (icc, length, offset + 8); + double wY = load_u16f16 (icc, length, offset + 8 + 4); + double wZ = load_u16f16 (icc, length, offset + 8 + 4 * 2); return babl_space_rgb_chromaticities (NULL, wX / (wX + wY + wZ), @@ -228,27 +252,27 @@ babl_space_rgb_icc (const char *icc, } } - else if (icc_tag (icc, "rXYZ", NULL, NULL) && - icc_tag (icc, "gXYZ", NULL, NULL) && - icc_tag (icc, "bXYZ", NULL, NULL)) + else if (icc_tag (icc, length, "rXYZ", NULL, NULL) && + icc_tag (icc, length, "gXYZ", NULL, NULL) && + icc_tag (icc, length, "bXYZ", NULL, NULL)) { int offset, element_size; double rx, gx, bx; double ry, gy, by; double rz, gz, bz; - icc_tag (icc, "rXYZ", &offset, &element_size); - rx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); - ry = load_u16Fixed16 (icc, offset + 8 + 4 * 1); - rz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); - icc_tag (icc, "gXYZ", &offset, &element_size); - gx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); - gy = load_u16Fixed16 (icc, offset + 8 + 4 * 1); - gz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); - icc_tag (icc, "bXYZ", &offset, &element_size); - bx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); - by = load_u16Fixed16 (icc, offset + 8 + 4 * 1); - bz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + icc_tag (icc, length, "rXYZ", &offset, &element_size); + rx = load_u16f16 (icc, length, offset + 8 + 4 * 0); + ry = load_u16f16 (icc, length, offset + 8 + 4 * 1); + rz = load_u16f16 (icc, length, offset + 8 + 4 * 2); + icc_tag (icc, length, "gXYZ", &offset, &element_size); + gx = load_u16f16 (icc, length, offset + 8 + 4 * 0); + gy = load_u16f16 (icc, length, offset + 8 + 4 * 1); + gz = load_u16f16 (icc, length, offset + 8 + 4 * 2); + icc_tag (icc, length, "bXYZ", &offset, &element_size); + bx = load_u16f16 (icc, length, offset + 8 + 4 * 0); + by = load_u16f16 (icc, length, offset + 8 + 4 * 1); + bz = load_u16f16 (icc, length, offset + 8 + 4 * 2); return babl_space_rgb_matrix (NULL, rx, gx, bx, -- 2.30.2